package com.google.code.irouter.model;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanMap;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.code.irouter.utils.DateTimeUtil;

public abstract class Model implements Serializable {

	private static final long serialVersionUID = -4539365902093751599L;

	private static Set<Class<?>> VAILD;

	private static Set<String> INVAILD;

	private Key key;
	
	private Date createDate;
	
	private Date updateDate;

	public Key getKey() {
		return key;
	}

	public void setKey(Key key) {
		this.key = key;
	}
	
	public Date getCreateDate() {
		return createDate;
	}

	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}

	public Date getUpdateDate() {
		return updateDate;
	}

	public void setUpdateDate(Date updateDate) {
		this.updateDate = updateDate;
	}

	static {
		VAILD = new HashSet<Class<?>>();
		VAILD.add(boolean.class);
		VAILD.add(java.lang.Boolean.class);
		VAILD.add(com.google.appengine.api.datastore.ShortBlob.class);
		VAILD.add(com.google.appengine.api.datastore.Blob.class);
		VAILD.add(com.google.appengine.api.datastore.Category.class);
		VAILD.add(java.util.Date.class);
		VAILD.add(com.google.appengine.api.datastore.Email.class);
		VAILD.add(float.class);
		VAILD.add(java.lang.Float.class);
		VAILD.add(double.class);
		VAILD.add(java.lang.Double.class);
		VAILD.add(com.google.appengine.api.datastore.GeoPt.class);
		VAILD.add(com.google.appengine.api.users.User.class);
		VAILD.add(short.class);
		VAILD.add(java.lang.Short.class);
		VAILD.add(int.class);
		VAILD.add(java.lang.Integer.class);
		VAILD.add(long.class);
		VAILD.add(java.lang.Long.class);
		VAILD.add(com.google.appengine.api.blobstore.BlobKey.class);
		VAILD.add(com.google.appengine.api.datastore.Key.class);
		VAILD.add(com.google.appengine.api.datastore.Link.class);
		VAILD.add(com.google.appengine.api.datastore.IMHandle.class);
		VAILD.add(com.google.appengine.api.datastore.PostalAddress.class);
		VAILD.add(com.google.appengine.api.datastore.Rating.class);
		VAILD.add(com.google.appengine.api.datastore.PhoneNumber.class);
		VAILD.add(java.lang.String.class);
		VAILD.add(com.google.appengine.api.datastore.Text.class);

		VAILD.add(java.util.HashSet.class);
		VAILD.add(java.util.ArrayList.class);

		INVAILD = new HashSet<String>();
		INVAILD.add(Class.class.getSimpleName().toLowerCase());
		INVAILD.add(Entity.class.getSimpleName().toLowerCase());
		INVAILD.add(Key.class.getSimpleName().toLowerCase());
	}

	public boolean parse(Entity entity) {
		boolean result = true;
		Map<String, Object> properties = entity.getProperties();
		Set<String> propertyKeySet = properties.keySet();
		for (String propertyKey : propertyKeySet) {
			Object propertyValue = properties.get(propertyKey);
			try {
				boolean isCancel = false;
				if(List.class.isInstance(propertyValue)) {
					List<?> listOfPropertyValue = (List<?>) propertyValue;
					String targetClassName = PropertyUtils.getPropertyDescriptor(this, propertyKey).getPropertyType().getName();
					if(Set.class.getName().equals(targetClassName)) {
						propertyValue = new HashSet<Object>(listOfPropertyValue);
					} else if(List.class.getName().equals(targetClassName)) {
						propertyValue = listOfPropertyValue;
					} else {
						isCancel = true;
					}
				}
				if(!isCancel) {
					if(propertyValue != null)
						BeanUtils.setProperty(this, propertyKey, propertyValue);
				}				
			} catch (IllegalAccessException e) {
				e.printStackTrace();
				result = false;
				break;
			} catch (InvocationTargetException e) {
				e.printStackTrace();
				result = false;
				break;
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
				result = false;
				break;
			}
		}
		this.setKey(entity.getKey());
		return result;
	}

	@SuppressWarnings("unchecked")
	public Entity getEntity() {
		String entityName = this.getClass().getSimpleName();
		Entity entity = new Entity(entityName);
		if (this.getKey() != null) {
			entity = new Entity(this.getKey());
		}
		BeanMap beanMap = new BeanMap(this);
		Set<String> keySet = beanMap.keySet();
		for (String key : keySet) {
			if (!INVAILD.contains(key)) {
				Object value = beanMap.get(key);
				if (value != null) {
					if (VAILD.contains(value.getClass())) {
						entity.setProperty(key, value);
					}
				} else {
					entity.setProperty(key, null);
				}
			}
		}
		return entity;
	}
	
	public void pushCreatDate() {
		this.createDate = DateTimeUtil.getNow();
		this.updateDate = DateTimeUtil.getNow();
	}
	
	public void pushUpdateDate() {
		this.updateDate = DateTimeUtil.getNow();
	}

}
